package com.nulldev.util.web.flexibleKit.flexRenderer;

import java.awt.Component;
import java.awt.Font;
import java.awt.Rectangle;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.swing.JFrame;
import javax.swing.JScrollPane;

import com.nulldev.util.JVM.Arguments;
import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.data.Variables;
import com.nulldev.util.data.VariableCaches.VariableCache;
import com.nulldev.util.graphics.renderIt.api.renderItAPI;
import com.nulldev.util.graphics.renderIt.api.renderItConstants;
import com.nulldev.util.graphics.renderIt.api.renderItGraphicsInterface;
import com.nulldev.util.graphics.renderIt.api.core.IRenderCallback;
import com.nulldev.util.graphics.renderIt.api.core.IRenderCallbackMetadata;
import com.nulldev.util.graphics.renderIt.api.core.IRenderCanvas;
import com.nulldev.util.graphics.renderIt.api.core.RITFont;
import com.nulldev.util.graphics.renderIt.api.core.renderItOutputType;
import com.nulldev.util.graphics.renderIt.api.envcaps.renderItCanvasOperations;
import com.nulldev.util.graphics.renderIt.api.envcaps.renderItOperations;
import com.nulldev.util.graphics.renderIt.ginterfaces.stub.StubGraphicsInterface;
import com.nulldev.util.graphics.renderIt.renderer.global.rColor;
import com.nulldev.util.graphics.renderIt.renderer.global.rColors;
import com.nulldev.util.graphics.renderIt.util.renderPosition;
import com.nulldev.util.gui.fontAPI.FontManager;
import com.nulldev.util.gui.swingAPI.Swing;
import com.nulldev.util.gui.themeAPI.Theme;
import com.nulldev.util.gui.themeAPI.ThemeAPI;
import com.nulldev.util.gui.themeAPI.ThemeAPI.ThemingOptions;
import com.nulldev.util.osAPI.OS;
import com.nulldev.util.web.HttpClient.HttpClient;
import com.nulldev.util.web.flexibleKit.general.RenderOperation;
import com.nulldev.util.web.flexibleKit.general.flexDocument;
import com.nulldev.util.web.flexibleKit.general.flexKit;
import com.nulldev.util.web.flexibleKit.general.flexDocument.DocumentFlags;
import com.nulldev.util.web.flexibleKit.jfvParse.Document;
import com.nulldev.util.web.flexibleKit.jfvParse.JFiveParse;
import com.nulldev.util.web.flexibleKit.jfvParse.Text;

public class FlexRenderer implements IRenderCallback {

	private final IRenderCanvas canvas;
	private final JScrollPane pane;

	public FlexRenderer(final IRenderCanvas canvas, final JScrollPane pane) {
		Variables.requireNonNull(canvas, "canvas");
		this.canvas = canvas;
		this.pane = pane;
	}

	private volatile flexDocument doc;
	public final AtomicBoolean DEBUG_RENDER_ELEMENT_HUD = new AtomicBoolean(true), ALLOW_VIEWPORT_CLIPPING = new AtomicBoolean(false), // Viewport clipping is
																																		// broken at the moment.
			ENABLE_STUB_RENDERER_DURING_IDLE = new AtomicBoolean(!OS.isLinux());
	private StubGraphicsInterface RENDER_STUB;

	public void bindDocument(final flexDocument fd) {
		if (fd == null)
			throw new NullPointerException("fd == null!");
		if (this.doc != null) {
			this.doc.destroy();
			this.RENDER_STUB = null;
		}
		this.doc = fd;
		this.doc.init();
	}

	public VariableCache getImageCache() {
		return this.doc != null ? this.doc.getEnvironment().imgCache : null;
	}

	public static void main(String[] args) throws Exception {
		Arguments.setup(args);
		Swing.enableAA();
		Swing.enableJSO();
		final String url = "https://www.howsmyssl.com/";
		final boolean useRealDemos = true;
		final Document d;
		if (useRealDemos) {
			d = JFiveParse.parse(HttpClient.getString().url(url).forceHeader("User-Agent", flexKit.getUserAgent()).create().dataThenClose());
		} else {
			final String o = "<html>\n" + "<h1>Hello from flex!</h1>\n" + "<h2>Now powered by renderIt " + renderItAPI.VERSION.asVersionString() + "!</h2>\n"
					+ "<h4>We should be small!</h4>\n" + "<style>p {color: red;}</style>" + "<img src=\"https://picsum.photos/200\"></img>\n"
					+ "<p>We are below an <strong>image</strong>!</p>" + "<br>" + "<p>We are below an <strong>image</strong>!</p>"
					+ "<h2 class=\"Headline\">[<font class=\"Subscription\">$</font>] [REDACTED] Weekly Edition for April 14, 2022</h2>" + "</html>";
			d = JFiveParse.parse(o);
		}
		final JFrame jfr = new JFrame("flexRenderer.");
		jfr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		final IRenderCanvas canvas = renderItAPI.make_instance(renderItOutputType.RIT_OUTPUT_TYPE_AWT_FRAME, jfr);
		final JScrollPane pane;
//		this.canvas.setRenderMethod(RenderMethod.REPAINT_WITHIN_SYNC);
		if (canvas.supportsOperation(renderItCanvasOperations.RIT_output_swing_component)) {
			pane = new JScrollPane((Component) canvas);
		} else if (canvas.supportsOperation(renderItCanvasOperations.RIT_output_awt_component)) {
			pane = new JScrollPane((Component) canvas);
		} else {
			throw new UnsupportedOperationException("RIT_output_swing_component AND RIT_output_awt_component is missing!");
		}
		pane.getVerticalScrollBar().setUnitIncrement(16);
		jfr.add(pane);
		jfr.setBounds(500, 500, 640, 480);
		jfr.setVisible(true);
		JVM.println("Canvas: " + canvas);
		try {
//			this.canvas.deferRendering(true);
			ThemeAPI.applySwingTheme(Theme.SwingTheming.SWING_LIGHT, pane, ThemingOptions.APPLY_SYSTEM_LOOK_AND_FEEL);
		} catch (Exception e) {
			JVM.errorln(e);
		}
		final FlexRenderer fr = new FlexRenderer(canvas, pane);
		canvas.setRenderCallback(fr);
		canvas.start();
		final flexDocument fd = new flexDocument(url, d, DocumentFlags.DISABLE_JS_PARSER);
		ExecutorManager.globalAsyncSubmit(() -> {
			fr.bindDocument(fd);
			return null;
		});
	}

	private boolean has_list_extensions = false;
	private boolean /* feature_ */
	feature_supports_vsync = false, feature_deferred_rendering = false, feature_paint_hints = false, feature_buffer_control = false,
			feature_text_anti_aliasing = false, feature_viewport_control = false, feature_viewport_clipping = false;
	private RITFont cfont_32, cfont_16;

	@Override
	public void onRender(final renderItGraphicsInterface iface, final IRenderCallbackMetadata metadata) {
		if (!iface.awt_hasRenderable()) {
			return;
		}
		if (!this.has_list_extensions) {
			this.has_list_extensions = true;
			JVM.println("Renderer: " + iface.rit_get(renderItConstants.RIT_RENDERER_NAME));
			JVM.println("Renderer Mode: " + renderItConstants.TO_STRING(iface.rit_get(renderItConstants.RIT_BACKEND_GET_RENDERER_TYPE)));
			if (iface.supportsOperations(renderItOperations.NITROUS_accel_info))
				JVM.println("Nitrous Backend: " + iface.nitrous_getBackend());
			JVM.println("Printing extensions.");
			ArrayUtils.println(iface.supportedOperations());
			this.feature_supports_vsync = iface.supportsOperations(renderItOperations.RIT_EXT_vsync);
			if (this.feature_supports_vsync)
				iface.vsync_set(renderItConstants.RIT_VSYNC_ADAPTIVE);
			this.feature_deferred_rendering = iface.supportsOperations(renderItOperations.RIT_EXT_deferred_rendering);
			this.feature_paint_hints = iface.supportsOperations(renderItOperations.RIT_EXT_paint_hints);
			this.feature_buffer_control = iface.supportsOperations(renderItOperations.RIT_EXT_buffer_control);
			this.feature_text_anti_aliasing = iface.supportsOperations(renderItOperations.RIT_text_anti_aliasing);
			this.feature_viewport_control = iface.supportsOperations(renderItOperations.RIT_EXT_viewport_control);
			this.feature_viewport_clipping = iface.supportsOperations(renderItOperations.RIT_LCDUI_clip);
			JVM.println("Features: ");
			if (this.feature_supports_vsync)
				JVM.println("FEATURE_VSYNC");
			if (this.feature_deferred_rendering)
				JVM.println("FEATURE_DEFERRED_RENDERING");
			if (this.feature_paint_hints)
				JVM.println("FEATURE_PAINT_HINTS");
			if (this.feature_buffer_control)
				JVM.println("FEATURE_BUFFER_CONTROL");
			if (this.feature_text_anti_aliasing)
				JVM.println("FEATURE_TEXT_ANTI_ALIASING");
			if (this.feature_viewport_control)
				JVM.println("FEATURE_VIEWPORT_CONTROL");
			if (this.feature_viewport_clipping)
				JVM.println("FEATURE_VIEWPORT_CLIPPING");

			if (this.feature_deferred_rendering)
				iface.rit_dr_setEnabled(true);
		}
		if (cfont_32 == null) {
			cfont_32 = iface.awt_make_font(FontManager.getFontSetAsAWT(32));
		}
		if (cfont_16 == null) {
			cfont_16 = iface.awt_make_font(FontManager.getFontSetAsAWT(16));
		}
		if (this.feature_text_anti_aliasing && !iface.text_isAAPresent())
			iface.text_setAAPresence(true);
		if (doc != null) {
			doc.getEnvironment().fontCache.init(iface);
			if (doc.isParsing()) {
				iface.bd_fillRect(0, iface.viewport_getHeight() - 50, iface.viewport_getWidth(), 50, rColors.gray);
				iface.text_shadowedString("Parsing... (stage: " + doc.getStage() + ")", 5, iface.viewport_getHeight() - 25, cfont_16, rColors.white);
				if (this.feature_deferred_rendering)
					iface.rit_dr_requestPaint();
				return;
			}
//			final int fontSize = g.getWidth() / 32;
			final renderPosition rp = new renderPosition(5, 25);
			if (this.feature_viewport_clipping && ALLOW_VIEWPORT_CLIPPING.get()) {
				final Rectangle r = this.pane.getViewportBorderBounds();
				iface.lcdui_clip_set(r.x, r.y, r.width, r.height);
			}
			for (final RenderOperation ri : doc.renderPath()) {
				final Font f = (Font) iface.awt_getCurrentFont();
				if ((this.canvas.canvas_getHeight() > rp.getY() + 360 || this.canvas.canvas_getHeight() < rp.getY() - iface.viewport_getHeight())
						&& ENABLE_STUB_RENDERER_DURING_IDLE.get()) {
					if (this.RENDER_STUB == null) {
						this.RENDER_STUB = new StubGraphicsInterface(iface);
					}
					ri.doRender(this.RENDER_STUB, rp, doc.getEnvironment());
				} else {
					final renderPosition pre = rp.clone();
					final renderPosition post = ri.doRender(iface, rp, doc.getEnvironment());
					final int dX = post.getX();
					final int dY = post.getY();
					if (DEBUG_RENDER_ELEMENT_HUD.get()) {
						iface.bd_drawRect(pre.getX(), pre.getY(), dX, dY, rColors.red);
						String node_name = ri.operationFor().getNodeName();
						if (ri.operationFor() instanceof Text)
							node_name = ri.operationFor().getParentNode().getNodeName();
						iface.text_shadowedString(ri.toString() + "[w=" + dX + ",h=" + dY + ",el=" + node_name + "]", pre.getX(), pre.getY() + 10,
								iface.awt_make_font(f).deriveToSize(16), rColors.red);
						iface.setColor(rColor.black);
					}
					if (ri.keepInline()) {
						rp.setY(pre.getY());
					}
				}
//				g.runQuirkFix(RenderingQuirks.RESET_FONT_AFTER_EACH_OP, f);
//				g.getPostQuirks().forEachRemaining((quirk) -> {
//					switch (quirk) {
//						case RESET_X_AFTER_CALL:
//							rp.setX(5);
//							break;
//						default:
//							break;
//					}
//				});
			}
			if (this.feature_viewport_control) {
				iface.viewport_requestSize(rp.getX(), rp.getY());
			}
		} else {
			iface.text_shadowedString("Loading content...", 5, 25, cfont_32, rColors.wheat);
			iface.text_shadowedString("This should only take a moment.", 5, 45, cfont_16, rColors.wheat);
			if (this.feature_deferred_rendering)
				iface.rit_dr_requestPaint();
		}
		if (this.feature_supports_vsync) {
			iface.vsync_syncToDisplay(0);
		}
	}
}
